﻿// VsPkg.cs : Implementation of VisualStudio
//

using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.Win32;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio;
using NBusiness.VisualStudio.Language;

namespace NBusiness.VisualStudio
{
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [DefaultRegistryRoot(@"Software\Microsoft\VisualStudio\8.0Exp")]
    [ProvideLoadKey("Standard", "2.0", "Visual E#", "none", 114)]
    [Guid(Guids.guidNBusinessPackageString)]
    [ProvideMenuResource(1000, 1)]
    [ProvideService(typeof(ESharpLanguageService), ServiceName = "ESharp")]
    [ProvideService(typeof(IESharpLibraryManager))]
    [ProvideLanguageService(typeof(ESharpLanguageService), 
        "ESharp",
        110,
        CodeSense=true,
        DefaultToInsertSpaces=true,
        EnableCommenting=true,
        MatchBraces=true,
        ShowCompletion=true,
        ShowMatchingBrace=true)]
    [ProvideLanguageExtension(typeof(ESharpLanguageService), ".es")]
    [InstalledProductRegistration(true, "#110", "#112", "2.0", IconResourceID = 400)]
    [ProvideEditorExtensionAttribute(typeof(ESharpEditorFactory), ".es", 32, NameResourceID = 300)]
    [ProvideEditorLogicalView(typeof(EditorFactory), "{7651a702-06e5-11d1-8ebd-00a0c90f26ea}")]  //LOGVIEWID_Designer
    [ProvideEditorLogicalView(typeof(EditorFactory), "{7651a701-06e5-11d1-8ebd-00a0c90f26ea}")]  //LOGVIEWID_Code
    [ProvideProjectItem(typeof(ESharpProjectFactory),
        "Visual E# Items",
        ".\\null",
       1)]
    [ProvideProjectFactory(typeof(ESharpProjectFactory),
         "Visual E#",
         "E# Project Files (*.esproj);*.esproj",
         "esproj",
         "esproj",
         ".\\null",
         LanguageVsTemplate = "ESharp")]
    [SingleFileGeneratorSupportRegistrationAttribute(typeof(ESharpProjectFactory))]
    [ProvideObject(typeof(ESharpPropertyPage))]
    [ProvideObject(typeof(ESharpBuildPropertyPage))]
    public sealed class ESharpPackage : ProjectPackage, IOleComponent, IVsInstalledProduct
    {
        private uint componentID;
        private ESharpLibraryManager libraryManager;
        internal ESharpLibraryManager LibraryManager
        {
            get { return libraryManager; }
        }

        private ESharpLanguageService _languageService;
        public ESharpLanguageService LanguageService
        {
            get { return _languageService; }
        }

        /// <summary>
        /// Default constructor of the package.
        /// Inside this method you can place any initialization code that does not require 
        /// any Visual Studio service because at this point the package object is created but 
        /// not sited yet inside Visual Studio environment. The place to do all the other 
        /// initialization is the Initialize method.
        /// </summary>
        public ESharpPackage()
        {
            IServiceContainer container = this as IServiceContainer;
            ServiceCreatorCallback callback = new ServiceCreatorCallback(CreateService);
            container.AddService(typeof(IESharpLibraryManager), callback, true);
            container.AddService(typeof(ESharpLanguageService), callback, true);

            //Initialize it!
            CreateService(null, typeof(ESharpLanguageService));
        }

        protected override void Initialize()
        {
            base.Initialize();

            this.RegisterProjectFactory(new ESharpProjectFactory(this));
            this.RegisterEditorFactory(new ESharpEditorFactory(this));
        }

        private object CreateService(IServiceContainer container, Type serviceType)
        {
            object service = null;
            if (typeof(ESharpLanguageService) == serviceType)
            {
                //Unfortunately the order that the services get instantiated cannot be explictly set
                //and since the language service is dependent upon the library manager we need to
                //instantiate the library manager first just in case. It "should" get called for
                //creation next.
                if (libraryManager == null)
                    libraryManager = new ESharpLibraryManager(this);

                _languageService = new ESharpLanguageService(libraryManager);
                _languageService.SetSite(this);
                service = _languageService;
            }
            else if (typeof(IESharpLibraryManager) == serviceType)
            {
                if(libraryManager == null)
                    libraryManager = new ESharpLibraryManager(this);

                service = libraryManager as IESharpLibraryManager;
            }
            
            return service;
        }

        private void RegisterForIdleTime()
        {
            IOleComponentManager mgr = GetService(typeof(SOleComponentManager)) as IOleComponentManager;
            if (componentID == 0 && mgr != null)
            {
                OLECRINFO[] crinfo = new OLECRINFO[1];
                crinfo[0].cbSize = (uint)Marshal.SizeOf(typeof(OLECRINFO));
                crinfo[0].grfcrf = (uint)_OLECRF.olecrfNeedIdleTime |
                                              (uint)_OLECRF.olecrfNeedPeriodicIdleTime;
                crinfo[0].grfcadvf = (uint)_OLECADVF.olecadvfModal |
                                              (uint)_OLECADVF.olecadvfRedrawOff |
                                              (uint)_OLECADVF.olecadvfWarningsOff;
                crinfo[0].uIdleTimeInterval = 1000;
                int hr = mgr.FRegisterComponent(this, crinfo, out componentID);
            }
        }

        protected override void Dispose(bool disposing)
        {
            try
            {
                if (componentID != 0)
                {
                    IOleComponentManager mgr = GetService(typeof(SOleComponentManager)) as IOleComponentManager;
                    if (mgr != null)
                    {
                        mgr.FRevokeComponent(componentID);
                    }
                    componentID = 0;
                }
                if (null != libraryManager)
                {
                    libraryManager.Dispose();
                    libraryManager = null;
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }

        #region IOleComponent Members

        public int FContinueMessageLoop(uint uReason, IntPtr pvLoopData, MSG[] pMsgPeeked)
        {
            return 1;
        }

        public int FDoIdle(uint grfidlef)
        {
            ESharpLanguageService pl = GetService(typeof(ESharpLanguageService)) as ESharpLanguageService;
            if (pl != null)
            {
                pl.OnIdle((grfidlef & (uint)_OLEIDLEF.oleidlefPeriodic) != 0);
            }
            if (null != libraryManager)
            {
                libraryManager.OnIdle();
            }
            return 0;
        }

        public int FPreTranslateMessage(MSG[] pMsg)
        {
            return 0;
        }

        public int FQueryTerminate(int fPromptUser)
        {
            return 1;
        }

        public int FReserved1(uint dwReserved, uint message, IntPtr wParam, IntPtr lParam)
        {
            return 1;
        }

        public IntPtr HwndGetWindow(uint dwWhich, uint dwReserved)
        {
            return IntPtr.Zero;
        }

        public void OnActivationChange(IOleComponent pic, int fSameComponent, OLECRINFO[] pcrinfo, int fHostIsActivating, OLECHOSTINFO[] pchostinfo, uint dwReserved)
        {
        }

        public void OnAppActivate(int fActive, uint dwOtherThreadID)
        {
        }

        public void OnEnterState(uint uStateID, int fEnter)
        {
        }

        public void OnLoseActivation()
        {
        }

        public void Terminate()
        {
        }

        #endregion

        #region IVsInstalledProduct Members
        /// <summary>
        /// This method is called during Devenv /Setup to get the bitmap to
        /// display on the splash screen for this package.
        /// </summary>
        /// <param name="pIdBmp">The resource id corresponding to the bitmap to display on the splash screen</param>
        /// <returns>HRESULT, indicating success or failure</returns>
        public int IdBmpSplash(out uint pIdBmp)
        {
            pIdBmp = 0;

            return VSConstants.S_OK;
        }

        /// <summary>
        /// This method is called to get the icon that will be displayed in the
        /// Help About dialog when this package is selected.
        /// </summary>
        /// <param name="pIdIco">The resource id corresponding to the icon to display on the Help About dialog</param>
        /// <returns>HRESULT, indicating success or failure</returns>
        public int IdIcoLogoForAboutbox(out uint pIdIco)
        {
            pIdIco = 400;

            return VSConstants.S_OK;
        }

        /// <summary>
        /// This methods provides the product official name, it will be
        /// displayed in the help about dialog.
        /// </summary>
        /// <param name="pbstrName">Out parameter to which to assign the product name</param>
        /// <returns>HRESULT, indicating success or failure</returns>
        public int OfficialName(out string pbstrName)
        {
            pbstrName = GetResourceString("@ProductName");
            return VSConstants.S_OK;
        }

        /// <summary>
        /// This methods provides the product description, it will be
        /// displayed in the help about dialog.
        /// </summary>
        /// <param name="pbstrProductDetails">Out parameter to which to assign the description of the package</param>
        /// <returns>HRESULT, indicating success or failure</returns>
        public int ProductDetails(out string pbstrProductDetails)
        {
            pbstrProductDetails = GetResourceString("@ProductDetails");
            return VSConstants.S_OK;
        }

        /// <summary>
        /// This methods provides the product version, it will be
        /// displayed in the help about dialog.
        /// </summary>
        /// <param name="pbstrPID">Out parameter to which to assign the version number</param>
        /// <returns>HRESULT, indicating success or failure</returns>
        public int ProductID(out string pbstrPID)
        {
            pbstrPID = GetResourceString("@ProductID");
            return VSConstants.S_OK;
        }

        /// <summary>
        /// This method loads a localized string based on the specified resource.
        /// </summary>
        /// <param name="resourceName">Resource to load</param>
        /// <returns>String loaded for the specified resource</returns>
        public string GetResourceString(string resourceName)
        {
            string resourceValue;
            IVsResourceManager resourceManager = (IVsResourceManager)GetService(typeof(SVsResourceManager));
            if (resourceManager == null)
            {
                throw new InvalidOperationException("Could not get SVsResourceManager service. Make sure the package is Sited before calling this method");
            }
            Guid packageGuid = this.GetType().GUID;
            int hr = resourceManager.LoadResourceString(ref packageGuid, -1, resourceName, out resourceValue);
            Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(hr);
            return resourceValue;
        }
        #endregion
    }
}